use axum::{routing::post, Json, Router};

use common_base::{
    prelude::*,
    serde::{self, Deserialize, Serialize},
    serde_json,
};
use common_core::{
    data::{DataObject, ParamObject, ResponseObject},
    InvokerManager,
};

pub fn registry_route() -> Router {
    Router::new().route("/api/common/service", post(service))
}

#[derive(Deserialize)]
#[serde(crate = "self::serde")]
#[serde(rename_all = "camelCase")]
pub struct CommonServiceRequest {
    service_type: String,
    service_key: String,
    object_name: Option<String>,
    object_method: Option<String>,
    param_object: serde_json::Value,
    data_object: serde_json::Value,
    custom_object: Option<serde_json::Value>,
    addon_object: Option<serde_json::Value>,
}

#[derive(Serialize)]
#[serde(crate = "self::serde")]
#[serde(rename_all = "camelCase")]
pub struct CommonServiceResponse {
    code: u32,
    message: &'static str,
    data: serde_json::Value,
}

///
async fn service(Json(request): Json<CommonServiceRequest>) -> Json<CommonServiceResponse> {
    // deserizlize only env_root and parameter_root
    let param_object = ParamObject::deserizlize_from_json(request.param_object);
    if let Err(e) = param_object {
        warn!("/api/common/service faield, cause: {}", e);
        return Json(create_failed_result("deserizlize param_object failed.", e));
    }
    let mut param_object = param_object.unwrap();

    // if !param_object.get_json_type_limit().is_empty() {
    // TODO: process custom json type deserizlize
    // }

    // if `cors` has set, use `serverID` as EAIServer, then refresh db_params
    if param_object.get_bool_by_param_name("serverCors", false) {
        let server_id = param_object.get_value_by_param_name("serverID");
        if server_id.is_empty() {
            let e = ErrorCode::EaiServerNotFound("");
            warn!("/api/common/service faield, cause: {}", e);
            return Json(create_failed_result(
                "serverID can not be none when serverCors is true.",
                e,
            ));
        };

        let server_id = server_id.to_owned();
        if let Err(e) = param_object.set_eai_server_then_refresh_db_param(server_id.as_str()) {
            warn!("/api/common/service faield, cause: {}", e);
            return Json(create_failed_result(
                "can not find EAIServer use serverID when serverCors is true.",
                e,
            ));
        }
    } else {
        // TODO: set http_servlet_request/http_servlet_response for param_object
    }

    let data_object = match DataObject::deserizlize_from_json(request.data_object) {
        Ok(data_obj) => data_obj,
        Err(e) => {
            warn!("/api/common/service faield, cause: {}", e);
            return Json(create_failed_result("/api/common/service faield", e));
        }
    };

    // TODO: custome_object and addon_object deserizlize

    // TODO: use commonServiceParamConvert to convert param_object

    // TODO: circular reference detect

    // TODO: sql templete replace

    // TODO: execute server script

    // TODO: set param_object to threadlocal

    let response_object = match request.service_type.as_str() {
        "IOM" => {
            InvokerManager::iom_as_svr(
                request
                    .object_name
                    .as_ref()
                    .map(|o| o.as_str())
                    .unwrap_or(""),
                request
                    .object_method
                    .as_ref()
                    .map(|m| m.as_str())
                    .unwrap_or(""),
                param_object,
                data_object,
            )
            .await
        }
        "SVR" => InvokerManager::svr(request.service_key.as_str(), param_object, data_object).await,
        _ => {
            let e = ErrorCode::UnImplement("invalid serviceType");
            warn!("/api/common/service faield, cause: {}", e);
            return Json(create_failed_result(
                "unsupport service type, use 'SVR' or 'IOM' instead.",
                e,
            ));
        }
    };
    match response_object {
        Ok(response_object) => Json(create_success_result("success", response_object)),
        Err(e) => {
            warn!("/api/common/service faield, cause: {}", e);
            Json(create_failed_result("/api/common/service faield", e))
        }
    }
}

fn create_success_result(message: &'static str, data: ResponseObject) -> CommonServiceResponse {
    CommonServiceResponse {
        code: 1,
        message,
        data: data.serializes_into_json(),
    }
}

fn create_failed_result(message: &'static str, e: ErrorCode) -> CommonServiceResponse {
    CommonServiceResponse {
        code: 0,
        message,
        data: serde_json::Value::String(e.message()),
    }
}

// this is for performance test
pub fn deserizlize_request_json(json: &str) {
    let request: CommonServiceRequest = serde_json::from_str(&json).unwrap();
    let param_object =
        ParamObject::deserizlize_from_json(request.param_object).expect("po deserizlize failed");
    let data_object =
        DataObject::deserizlize_from_json(request.data_object).expect("do deserizlize failed");
}
